
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "pub_lib.c"
#include "pidfile.h"

//openssl genrsa -out privkey.pem 2048
//openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095

#define PidFile  "/var/run/register.pid"

int process_client_string(struct pack_head * head, char *string)
{
	do_log2("USER_REGISTER_INFO:%s\n",string);
	return 0;
}

int process_client_session(int client_fd, SSL_CTX *ctx)
{
	int len = 0, total = 0;
	char * pBuf = NULL;
	SSL *ssl;
	struct pack_head head;
	
	ssl = SSL_new(ctx);
	SSL_set_fd(ssl, client_fd);
	if (SSL_accept(ssl) == -1) {
		do_log("ERROR! SSL_accept function error.");
		goto finish;
	}

	while (1)
	{
		int r, nfds = 0;
		struct timeval tv;
		fd_set rd;
		
		tv.tv_sec = TIMEOUT;
		tv.tv_usec = 0;

		FD_ZERO (&rd);
		FD_SET (client_fd, &rd);
		nfds = MAX_S(nfds, client_fd);
		
		r = select (nfds + 1, &rd, NULL, NULL, &tv);
		
		if (FD_ISSET (client_fd, &rd)) 
		{
			bzero(&head, sizeof(struct pack_head));
			len = SSL_read(ssl, &head, sizeof(struct pack_head));
			if (len <= 0 ){
				if (SSL_RECEIVED_SHUTDOWN == SSL_get_shutdown(ssl) || 
				    SSL_SENT_SHUTDOWN ==  SSL_get_shutdown(ssl))
					goto finish;
				else
				{
					do_log("ERROR! errno:%d, error string:'%s'\n", errno, strerror(errno));
					goto finish;
				}	
			}
			
			pBuf = (char *)malloc(head.len);
			if (!pBuf)
			{
				do_log("ERROR! errno:%d, error string:'%s'\n", errno, strerror(errno));
				goto finish;
			}
			bzero(pBuf, head.len);
			total = 0;
			len = 0;
			while (total != head.len)
			{
				len = SSL_read(ssl,pBuf + len, head.len);
				total += len;
			}
			pBuf[total] = '\0';
			process_client_string(&head,pBuf);
			free(pBuf);
		}
	}
	finish:
		SSL_shutdown(ssl);
		SSL_free(ssl);
		close(client_fd);

	return 0;
}

void sig_chld(int signo)
{
	pid_t 	pid;
	int	stat;

	while (( pid = waitpid(-1, &stat, WNOHANG)) > 0)
		do_log("Child %d terminalted.\n",pid);
	return ;
}
void doexit(sig)
	int sig;
{
	exit (0);
}

int main(int argc, char **argv)
{
		int sockfd, client_fd, Debug = 0;
		unsigned int server_port, listen_num;
		struct sockaddr_in server_addr, client_addr;
		SSL_CTX *ctx;
		socklen_t len;

		char SendCert[MAXBUF + 1] = "cacert.pem";
		char PrivCert[MAXBUF + 1] = "privkey.pem";

		pid_t ppid = getpid();
		if (!Debug)
		{
			do_log("Checking pidfile.\n");
			if (!check_pid(PidFile))
			{
				int num_fds = 0;
				int i = 0;
				if (fork()) {
					/*
					 * Parent process
					 */
					exit(1);
				}
				num_fds = getdtablesize();
				for (i= 0; i < num_fds; i++)
					(void) close(i);
				setsid();
				do_log("Writing pidfile.\n");
				if (!check_pid(PidFile))
				{
					if (!write_pid(PidFile))
					{
						do_log("Can't write pid.\n");
						exit(1);
					}
				}
				else
				{
					do_log("Pidfile (and pid) already exist.\n");
					exit(1);
				}
			}
			else
			{
				do_log("Register Server: Already running.\n");
				exit(1);
			}
		}
		do_log("Register Server: Running back.\n");

		server_port = SERVER_PORT;
		listen_num = SERVER_LISTEN;
		
		signal(SIGCHLD,sig_chld);
		signal(SIGHUP,doexit);

		SSL_library_init();
		OpenSSL_add_all_algorithms();
		SSL_load_error_strings();
		ctx = SSL_CTX_new(SSLv23_server_method());
		if (ctx == NULL) {
			ERR_print_errors_fp(stdout);
			exit(1);
		}
		/* load user cert and the cert send to client and the cert include pub key. */
		if (SSL_CTX_use_certificate_file(ctx, SendCert, SSL_FILETYPE_PEM) <= 0) {
			ERR_print_errors_fp(stdout);
			exit(1);
		}
		/* load private cert. */
		if (SSL_CTX_use_PrivateKey_file(ctx, PrivCert, SSL_FILETYPE_PEM) <= 0) {
			ERR_print_errors_fp(stdout);
			exit(1);
		}
		chdir ("/");
		if (!SSL_CTX_check_private_key(ctx)) {
			ERR_print_errors_fp(stdout);
			exit(1);
		}
		if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
			do_log("socket function error.");
			exit(1);
		}

		bzero(&server_addr, sizeof(server_addr));
		server_addr.sin_family = PF_INET;
		server_addr.sin_port = htons(server_port);
		server_addr.sin_addr.s_addr = INADDR_ANY;

		if (bind(sockfd, (struct sockaddr *) &server_addr, sizeof(struct sockaddr)) == -1) {
			do_log("bind function error.");
			exit(1);
		}

		if (listen(sockfd, listen_num) == -1) {
			do_log("listen function error.");
			exit(1);
		}

		while (1) {
		        int r, nfds = 0;
			struct timeval tv;
			fd_set rd;
			pid_t pid = 0;
			
			tv.tv_sec = TIMEOUT;
			tv.tv_usec = 0;

			FD_ZERO (&rd);
		      	FD_SET (sockfd, &rd);
			nfds = MAX_S(nfds, sockfd);
			
			r = select (nfds + 1, &rd, NULL, NULL, &tv);
  			
			if (FD_ISSET (sockfd, &rd)) {
				len = sizeof(struct sockaddr);
				if ((client_fd = accept(sockfd, (struct sockaddr *) &client_addr, &len)) == -1) {
					do_log("accept function error.");
				}        
				do_log("accept client address:%s and port:%d.", 
					inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
				if ((pid=fork())>0){ // parent
					do_log("Forked, pid=%i. ", pid);
					close(client_fd);
				}
				else{
					close(sockfd);
					process_client_session(client_fd, ctx);
					SSL_CTX_free(ctx);
					exit(0);
				}
			}
		}
	close(sockfd);
	SSL_CTX_free(ctx);
	return 0;
}

